﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using ttbit.form.unity;
using ttbit.form;
using UnityEngine.Rendering;
using UnityEngine;

namespace ttbit.form.unity
{
    class Batch
    {
        ITexture tex;
        Effect effect;
        MeshTopology meshTopology;
        Transform tran;
        MeshRenderer meshrender;
        Mesh mesh;
        int layerid;
        public Batch(ITexture tex, Effect effect, MeshTopology meshTopology, Transform tran, int layerid)
        {
            this.tex = tex;
            this.effect = effect;
            this.tran = tran;
            pricount = 0;
            GameObject obj = new GameObject("batch");
            obj.transform.parent = tran;
            obj.transform.localPosition = new Vector3(0, 0, layerid);
            var mf = obj.AddComponent<MeshFilter>();
            meshrender = obj.AddComponent<MeshRenderer>();
            meshrender.material = GenMaterial();
            mf.mesh = mesh = new Mesh();
            mesh.vertices = new Vector3[40000];
            mesh.triangles = new int[60000];
            mesh.uv = new Vector2[40000];
            mesh.colors32 = new Color32[40000];
            mesh.UploadMeshData(false);

            mesh.SetIndexBufferParams(60000, IndexFormat.UInt32);
            VertexAttributeDescriptor[] vas = new VertexAttributeDescriptor[3]
            {
                   new VertexAttributeDescriptor(VertexAttribute.Position,VertexAttributeFormat.Float32,3,0),
                    new VertexAttributeDescriptor(VertexAttribute.Color,VertexAttributeFormat.UNorm8,4,0),
                    new VertexAttributeDescriptor(VertexAttribute.TexCoord0,VertexAttributeFormat.Float32,2,0),
            };
            mesh.SetVertexBufferParams(40000, vas);
            mesh.bounds = new Bounds(Vector3.zero, new Vector3(100, 100, 100));
            mesh.MarkDynamic();
            //unsafe
            //{
            //    Debug.LogWarning("sizeof(v=" + sizeof(vertexbuffer));
            //    Debug.LogWarning("c=" + mesh.GetVertexAttributeOffset(VertexAttribute.Color));
            //    ptrPos = (vertexbuffer*)mesh.GetNativeVertexBufferPtr(0);
            //    ptrIndex = (uint*)mesh.GetNativeIndexBufferPtr();
            //}

            unsafe
            {
                fixed (vertexbuffer* p1 = poss)
                fixed (uint* p2 = tris)
                {
                    ptrPos = p1;// (vertexbuffer*)poss; mesh.GetNativeVertexBufferPtr(0);
                    ptrIndex = p2;// (uint*)mesh.GetNativeIndexBufferPtr();
                }
            }

        }
        Material GenMaterial()
        {
            Shader diff = Shader.Find("Unlit/ttform.rgba");
            if (effect == null && tex.format == TextureFormat.R8)
            {
                diff = Shader.Find("Unlit/ttform.alpha");
            }
            var mat = new Material(diff);

            UnityEngine.Texture utex = null;
            if (tex is Texture2D tex2d)
            {
                utex = tex2d.tex;
            }
            else if (tex is RenderTarget rt)
            {
                utex = rt.rt;
            }
            mat.mainTexture = utex;
            mat.color = UnityEngine.Color.white;
            return mat;
        }
        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct vertexbuffer
        {
            [FieldOffset(0)]
            public Vector3 pos;
            [FieldOffset(12)]
            public Color32 color;
            [FieldOffset(16)]
            public Vector2 uv;

        }
        unsafe vertexbuffer* ptrPos;
        unsafe uint* ptrIndex;
        uint[] tris = new uint[6 * 10000];
        vertexbuffer[] poss = new vertexbuffer[4 * 10000];
        //Vector3[] poss = new Vector3[4 * 10000];
        //Color32[] colors = new Color32[4 * 10000];
        //Vector2[] uvs = new Vector2[4 * 10000];
        int pricount = 0;

        public void Clear()
        {
            SubMeshDescriptor sd = new SubMeshDescriptor();
            sd.topology = MeshTopology.Triangles;
            sd.vertexCount = 0;
            sd.indexStart = 0;
            sd.indexCount = 0;
            pricount = 0;
            mesh.SetSubMesh(0, sd);
            unsafe
            {
                fixed (vertexbuffer* p1 = poss)
                fixed (uint* p2 = tris)
                {
                    ptrPos = p1;// (vertexbuffer*)poss; mesh.GetNativeVertexBufferPtr(0);
                    ptrIndex = p2;// (uint*)mesh.GetNativeIndexBufferPtr();
                }
            }
        }
        public bool CanDraw(ITexture tex, Effect effect, MeshTopology topology)
        {
            if (pricount == 0)
            {
                this.tex = tex;
                this.effect = effect;
                this.meshTopology = topology;

                if (meshrender.material.mainTexture != (tex as ITextureNative).NativeTexture)
                    meshrender.material = GenMaterial();
                return true;
            }
            if (this.tex != tex || this.effect != effect || pricount >= 10000)
                return false;

            return true;
        }
        public void DrawRect(Rect rect, Rect uv, UnityEngine.Color32 color)
        {
            unsafe
            {
                var offset = 0;// 0.1f;
                ptrPos[pricount * 4 + 0].pos = new Vector3(rect.x, rect.y, layerid);
                ptrPos[pricount * 4 + 1].pos = new Vector3(rect.x + rect.width, rect.y, layerid);
                ptrPos[pricount * 4 + 2].pos = new Vector3(rect.x, rect.y + rect.height, layerid);
                ptrPos[pricount * 4 + 3].pos = new Vector3(rect.x + rect.width, rect.y + rect.height, layerid);
                ptrPos[pricount * 4 + 0].uv = new Vector2((uv.x - offset) / tex.width, (uv.y - offset) / tex.height);
                ptrPos[pricount * 4 + 1].uv = new Vector2((uv.x + uv.width- offset) / tex.width, (uv.y - offset) / tex.height);
                ptrPos[pricount * 4 + 2].uv = new Vector2((uv.x - offset) / tex.width, (uv.y + uv.height- offset) / tex.height);
                ptrPos[pricount * 4 + 3].uv = new Vector2((uv.x + uv.width- offset) / tex.width, (uv.y + uv.height- offset) / tex.height);
                ptrPos[pricount * 4 + 0].color = color;
                ptrPos[pricount * 4 + 1].color = color;
                ptrPos[pricount * 4 + 2].color = color;
                ptrPos[pricount * 4 + 3].color = color;

                ptrIndex[pricount * 6 + 0] = (ushort)(pricount * 4 + 0);
                ptrIndex[pricount * 6 + 1] = (ushort)(pricount * 4 + 2);
                ptrIndex[pricount * 6 + 2] = (ushort)(pricount * 4 + 1);
                ptrIndex[pricount * 6 + 3] = (ushort)(pricount * 4 + 1);
                ptrIndex[pricount * 6 + 4] = (ushort)(pricount * 4 + 2);
                ptrIndex[pricount * 6 + 5] = (ushort)(pricount * 4 + 3);
            }
            pricount++;
        }
        public void DrawRectFree(DrawPoint point0, DrawPoint point1, DrawPoint point2, DrawPoint point3)
        {
            unsafe
            {
                ptrPos[pricount * 4 + 0].pos = new Vector3(point0.pos.X, point0.pos.Y, point0.pos.Z);
                ptrPos[pricount * 4 + 1].pos = new Vector3(point1.pos.X, point1.pos.Y, point1.pos.Z);
                ptrPos[pricount * 4 + 2].pos = new Vector3(point2.pos.X, point2.pos.Y, point2.pos.Z);
                ptrPos[pricount * 4 + 3].pos = new Vector3(point3.pos.X, point3.pos.Y, point3.pos.Z);
                ptrPos[pricount * 4 + 0].uv = new Vector2(point0.uv.X / tex.width, point0.uv.Y / tex.height);
                ptrPos[pricount * 4 + 1].uv = new Vector2(point1.uv.X / tex.width, point1.uv.Y / tex.height);
                ptrPos[pricount * 4 + 2].uv = new Vector2(point2.uv.X / tex.width, point2.uv.Y / tex.height);
                ptrPos[pricount * 4 + 3].uv = new Vector2(point3.uv.X / tex.width, point3.uv.Y / tex.height);
                ptrPos[pricount * 4 + 0].color = new Color32(point0.color.R, point0.color.G, point0.color.B, point0.color.A);
                ptrPos[pricount * 4 + 1].color = new Color32(point1.color.R, point1.color.G, point1.color.B, point1.color.A);
                ptrPos[pricount * 4 + 2].color = new Color32(point2.color.R, point2.color.G, point2.color.B, point2.color.A);
                ptrPos[pricount * 4 + 3].color = new Color32(point3.color.R, point3.color.G, point3.color.B, point3.color.A);

                ptrIndex[pricount * 6 + 0] = (ushort)(pricount * 4 + 0);
                ptrIndex[pricount * 6 + 1] = (ushort)(pricount * 4 + 2);
                ptrIndex[pricount * 6 + 2] = (ushort)(pricount * 4 + 1);
                ptrIndex[pricount * 6 + 3] = (ushort)(pricount * 4 + 1);
                ptrIndex[pricount * 6 + 4] = (ushort)(pricount * 4 + 2);
                ptrIndex[pricount * 6 + 5] = (ushort)(pricount * 4 + 3);
            }
            pricount++;
        }
        public void DrawTris(DrawPoint[] tris, int tricount)
        {
            unsafe
            {
                for (var i = 0; i < tricount; i++)
                {
                    ptrPos[pricount * 3 + 0].pos = new Vector3(tris[i * 3 + 0].pos.X, tris[i * 3 + 0].pos.Y, tris[i * 3 + 0].pos.Z);
                    ptrPos[pricount * 3 + 1].pos = new Vector3(tris[i * 3 + 1].pos.X, tris[i * 3 + 1].pos.Y, tris[i * 3 + 1].pos.Z);
                    ptrPos[pricount * 3 + 2].pos = new Vector3(tris[i * 3 + 2].pos.X, tris[i * 3 + 2].pos.Y, tris[i * 3 + 2].pos.Z);
                    ptrPos[pricount * 3 + 0].uv = new Vector2(tris[i * 3 + 0].uv.X, tris[i * 3 + 0].uv.Y);
                    ptrPos[pricount * 3 + 1].uv = new Vector2(tris[i * 3 + 1].uv.X, tris[i * 3 + 1].uv.Y);
                    ptrPos[pricount * 3 + 2].uv = new Vector2(tris[i * 3 + 2].uv.X, tris[i * 3 + 2].uv.Y);
                    ptrPos[pricount * 3 + 0].color = new Color32(tris[i * 3 + 0].color.R, tris[i * 3 + 0].color.G, tris[i * 3 + 0].color.B, tris[i * 3 + 0].color.A);
                    ptrPos[pricount * 3 + 1].color = new Color32(tris[i * 3 + 1].color.R, tris[i * 3 + 1].color.G, tris[i * 3 + 1].color.B, tris[i * 3 + 1].color.A);
                    ptrPos[pricount * 3 + 2].color = new Color32(tris[i * 3 + 2].color.R, tris[i * 3 + 2].color.G, tris[i * 3 + 2].color.B, tris[i * 3 + 2].color.A);
                    ptrIndex[pricount * 3 + 0] = (ushort)(pricount * 3 + 0);
                    ptrIndex[pricount * 3 + 1] = (ushort)(pricount * 3 + 1);
                    ptrIndex[pricount * 3 + 2] = (ushort)(pricount * 3 + 2);
                    pricount++;
                }
            }
        }
        public void DrawLines(DrawPoint[] lines, int linecount)
        {
            unsafe
            {
                for (var i = 0; i < linecount; i++)
                {
                    var p0 = (lines[i * 2 + 0]);
                    var p1 = (lines[i * 2 + 1]);
                    ptrPos[pricount * 2 + 0].pos = new Vector3(p0.pos.X, p0.pos.Y, p0.pos.Z);
                    ptrPos[pricount * 2 + 1].pos = new Vector3(p1.pos.X, p1.pos.Y, p1.pos.Z);
                    ptrPos[pricount * 2 + 0].uv = new Vector2(p0.uv.X, p0.uv.Y);
                    ptrPos[pricount * 2 + 1].uv = new Vector2(p1.uv.X, p1.uv.Y);
                    ptrPos[pricount * 2 + 0].color = new Color32(p0.color.R, p0.color.G, p0.color.B, p0.color.A);
                    ptrPos[pricount * 2 + 1].color = new Color32(p1.color.R, p1.color.G, p1.color.B, p1.color.A);
                    ptrIndex[pricount * 2 + 0] = (ushort)(pricount * 2 + 0);
                    ptrIndex[pricount * 2 + 1] = (ushort)(pricount * 2 + 1);
                    pricount++;
                }
            }
        }
        public void DrawPoints(DrawPoint[] points, int pointcount)
        {
            unsafe
            {
                for (var i = 0; i < pointcount; i++)
                {
                    var p0 = (points[i]);
                    ptrPos[pricount].pos = new Vector3(p0.pos.X, p0.pos.Y, p0.pos.Z);
                    ptrPos[pricount].uv = new Vector2(p0.uv.X, p0.uv.Y);
                    ptrPos[pricount].color = new Color32(p0.color.R, p0.color.G, p0.color.B, p0.color.A);
                    ptrIndex[pricount] = (ushort)(pricount);
                    pricount++;
                }
            }
        }
        public void End()
        {


            SubMeshDescriptor sd = new SubMeshDescriptor();
            //sd.topology = this.meshTopology;// MeshTopology.Triangles;
            if (this.meshTopology == MeshTopology.Quads)
            {
                sd.topology = MeshTopology.Triangles;
                sd.vertexCount = pricount * 4;
                sd.indexStart = 0;
                sd.indexCount = pricount * 6;
            }
            else if (this.meshTopology == MeshTopology.Triangles)
            {
                sd.topology = MeshTopology.Triangles;
                sd.vertexCount = pricount * 3;
                sd.indexStart = 0;
                sd.indexCount = pricount * 3;
            }
            else if (this.meshTopology == MeshTopology.Lines)
            {
                sd.topology = MeshTopology.Lines;
                sd.vertexCount = pricount * 2;
                sd.indexStart = 0;
                sd.indexCount = pricount * 2;
            }
            else if (this.meshTopology == MeshTopology.Points)
            {
                sd.topology = MeshTopology.Points;
                sd.vertexCount = pricount * 1;
                sd.indexStart = 0;
                sd.indexCount = pricount * 1;
            }
            else
            {
                throw new Exception("error meshtopology");
            }
            mesh.SetVertexBufferData(poss, 0, 0, pricount * 4, 0, MeshUpdateFlags.DontRecalculateBounds);
            mesh.SetIndexBufferData(tris, 0, 0, pricount * 6, MeshUpdateFlags.DontRecalculateBounds);
            mesh.SetSubMesh(0, sd);


        }
    }

}
